import { defineComponent, nextTick, onMounted, PropType, ref } from 'vue';
import { Actions, CategoryOption, Options, ValueOption } from '@/components/Table/types';
import { getQueryCondition, Select, selectLikeWithEnabled } from '@/components/Table/hooks/useSelect';
import { ElMessage } from 'element-plus';
import { useSearch } from '@/components/Table/hooks/useSearch';
import { getTableOptions, getTableOptionsAsync } from '@/components/Table/hooks/useOptions';
import { usePagination } from '@/components/Table/hooks/usePagination';
import { RowEnabled, useTable } from '@/components/Table/hooks/useTable';
import { getActions, getActionsFromAuthority } from '@/components/Table/hooks/useActions';
import { BaseModel } from '@/services/core';
import { useStore } from 'vuex';
import { HttpUtil } from '@/utils/HttpUtil';
import { AssociationService, DataService } from '@/services/core/DataService';
import { Search } from '@element-plus/icons-vue';
import { TableModel } from '@/components/Table/ManageTable/model';

export interface AssociationTableProps<
  T,
  N,
  Id = T extends BaseModel<infer U> ? U : number,
  Nid = T extends BaseModel<infer U> ? U : number
> {
  id?: string;
  associationService: AssociationService<T, unknown, N, Id, unknown, Nid>;
  modelService: DataService<N, Nid>;
  defaultModel: N;
  tableOptions: Options<N>;
  tableActions?: Actions;
  tableName?: string;
  authorities?: string[];
}

export interface ElTable {
  doLayout: () => void;
}

export type AssociationTableRow = TableModel & RowEnabled;

export default defineComponent({
  name: 'AssociationTable',
  props: {
    id: String,
    associationService: {
      type: Object as PropType<AssociationService<TableModel, unknown, TableModel, unknown, unknown, unknown>>,
      required: true,
    },
    modelService: {
      type: Object as PropType<DataService<TableModel, unknown>>,
      required: true,
    },
    defaultModel: {
      type: Object as PropType<TableModel>,
      required: true,
    },
    tableOptions: {
      type: Object as PropType<Options<TableModel>>,
      required: true,
    },
    tableActions: Object as PropType<Actions>,
    tableName: String as PropType<string>,
    authorities: Array as PropType<string[]>,
  },
  setup(props: AssociationTableProps<TableModel, TableModel>) {
    const options = getTableOptions(props.tableOptions);
    let actions;
    if (props.authorities) {
      actions = getActionsFromAuthority(props.authorities, props.tableName);
    } else if (props.tableActions) {
      actions = getActions(props.tableActions);
    } else {
      const store = useStore();
      actions = getActionsFromAuthority(store.getters['account/info']?.authorities, props.tableName);
    }
    const { choice, query, clearQuery, currentChoiceOption } = useSearch<TableModel>(props.defaultModel, options);
    const { pageControl, setCurrent: _setCurrent, setPageSize: _setPageSize } = usePagination();
    const { setSort: _setSort, tableControl } = useTable<TableModel>();

    const select: Select<TableModel> = () => {
      return selectLikeWithEnabled(
        props.associationService,
        props.modelService,
        props.id,
        getQueryCondition({ choice, query }, pageControl, tableControl, options),
        pageControl,
        tableControl
      );
    };
    const setCurrent = async (current: number) => {
      if (_setCurrent(current)) {
        try {
          return await select();
        } catch (e) {
          // console.log(e.response);
          ElMessage.error((e as Error).message);
          return null;
        }
      } else {
        return null;
      }
    };
    const setPageSize = async (size: number) => {
      if (_setPageSize(size)) {
        try {
          return await select();
        } catch (e) {
          // console.log(e.response);
          ElMessage.error((e as Error).message);
          return null;
        }
      } else {
        return null;
      }
    };
    const setSort = (column: { prop: string; order: string }) => {
      _setSort(column);
      select().catch(e => {
        ElMessage.error((e as Error).message);
      });
    };

    onMounted(() => {
      getTableOptionsAsync(options).then(() => {
        select().catch(e => {
          ElMessage.error((e as Error).message);
        });
      });
    });

    const elTableRef = ref(undefined as unknown as ElTable);

    const isReadOnly = (
      option: ValueOption<unknown, unknown> | CategoryOption<unknown, unknown>,
      row: AssociationTableRow
    ) => {
      return typeof option.readOnly === 'function' ? option.readOnly(row) : option.readOnly;
    };

    const handleEnabled = async (id: string, enabled: boolean) => {
      try {
        if (enabled) {
          const model = props.associationService.model(props.id, id);
          try {
            await props.associationService.insert(model);
          } catch (e) {
            ElMessage.error((e as Error).message);
          } finally {
            select().catch(e => {
              ElMessage.error((e as Error).message);
            });
          }
        } else {
          const model = props.associationService.model(props.id, id);
          try {
            const resp = await props.associationService.selectOne(model);
            if (HttpUtil.isOk(resp)) {
              await props.associationService.deleteById(HttpUtil.getData(resp).id);
            }
          } catch (e) {
            ElMessage.error((e as Error).message);
          } finally {
            select().catch(e => {
              ElMessage.error((e as Error).message);
            });
          }
        }
      } catch (e) {
        ElMessage.error((e as Error).message);
      } finally {
        select().catch(e => {
          ElMessage.error((e as Error).message);
        });
      }
    };

    return {
      choice,
      query,
      pageControl,
      tableControl,
      options,
      actions,
      elTableRef,
      select,
      clearQuery,
      currentChoiceOption,
      setSort,
      handleEnabled,
      setCurrent,
      setPageSize,
      isReadOnly,
      icons: {
        Search,
      },
    };
  },
  watch: {
    async id(n: string | undefined, o: string | undefined) {
      console.log(o, n);
      try {
        // eslint-disable-next-line vue/valid-next-tick
        await nextTick(async () => {
          await this.select();
        });
        // eslint-disable-next-line no-empty
      } catch (e) {}
    },
  },
});
